using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;
using FastReport.Utils;
using System.Drawing.Design;

namespace FastReport
{
    /// <summary>
    /// Specifies the style of a border line.
    /// </summary>
    public enum LineStyle
    {
        /// <summary>
        /// Specifies a solid line. 
        /// </summary>
        Solid,

        /// <summary>
        /// Specifies a line consisting of dashes.
        /// </summary>
        Dash,

        /// <summary>
        /// Specifies a line consisting of dots. 
        /// </summary>
        Dot,

        /// <summary>
        /// Specifies a line consisting of a repeating pattern of dash-dot. 
        /// </summary>
        DashDot,

        /// <summary>
        /// Specifies a line consisting of a repeating pattern of dash-dot-dot. 
        /// </summary>
        DashDotDot,

        /// <summary>
        /// Specifies a double line. 
        /// </summary>
        Double,

        /// <summary>
        /// Specifies a custom line. 
        /// </summary>
        Custom
    }

    /// <summary>
    /// Specifies the sides of a border.
    /// </summary>
    [Flags]
    public enum BorderLines
    {
        /// <summary>
        /// Specifies no border lines.
        /// </summary>
        None = 0,

        /// <summary>
        /// Specifies the left border line.
        /// </summary>
        Left = 1,

        /// <summary>
        /// Specifies the right border line.
        /// </summary>
        Right = 2,

        /// <summary>
        /// Specifies the top border line.
        /// </summary>
        Top = 4,

        /// <summary>
        /// Specifies the bottom border line.
        /// </summary>
        Bottom = 8,

        /// <summary>
        /// Specifies all border lines.
        /// </summary>
        All = 15
    }

    /// <summary>
    /// Represents a single border line.
    /// </summary>
    [TypeConverter(typeof(FastReport.TypeConverters.FRExpandableObjectConverter))]
    public class BorderLine
    {
        #region Fields
        private Color color;
        private LineStyle style;
        private float width;
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets a color of the line.
        /// </summary>
        [Editor("FastReport.TypeEditors.ColorEditor, FastReport", typeof(UITypeEditor))]
        public Color Color
        {
            get { return color; }
            set { color = value; }
        }

        /// <summary>
        /// Gets or sets a style of the line.
        /// </summary>
        [DefaultValue(LineStyle.Solid)]
        [Editor("FastReport.TypeEditors.LineStyleEditor, FastReport", typeof(UITypeEditor))]
        public LineStyle Style
        {
            get { return style; }
            set { style = value; }
        }

        /// <summary>
        /// Gets or sets a width of the line, in pixels.
        /// </summary>
        [DefaultValue(1f)]
        public float Width
        {
            get { return width; }
            set { width = value; }
        }

        internal DashStyle DashStyle
        {
            get
            {
                DashStyle[] styles = new DashStyle[] {
          DashStyle.Solid, DashStyle.Dash, DashStyle.Dot, DashStyle.DashDot, DashStyle.DashDotDot, DashStyle.Solid };
                return styles[(int)Style];
            }
        }
        #endregion

        #region Private Methods
        private bool ShouldSerializeColor()
        {
            return Color != Color.Black;
        }

        internal bool ShouldSerialize()
        {
            return Width != 1 || Style != LineStyle.Solid || Color != Color.Black;
        }
        #endregion

        #region Public Methods
        internal BorderLine Clone()
        {
            BorderLine result = new BorderLine();
            result.Assign(this);
            return result;
        }

        internal void Assign(BorderLine src)
        {
            Color = src.Color;
            Style = src.Style;
            Width = src.Width;
        }

        /// <inheritdoc/>
        public override bool Equals(object obj)
        {
            BorderLine line = obj as BorderLine;
            return line != null && Width == line.Width && Color == line.Color && Style == line.Style;
        }

        /// <inheritdoc/>
        public override int GetHashCode()
        {
            return Color.GetHashCode() ^ Style.GetHashCode() ^ Width.GetHashCode();
        }

        internal void Draw(FRPaintEventArgs e, float x, float y, float x1, float y1,
          bool reverseGaps, bool gap1, bool gap2)
        {
            IGraphics g = e.Graphics;

            int penWidth = (int)Math.Round(Width * e.ScaleX);
            if (penWidth <= 0)
                penWidth = 1;
            using (Pen pen = new Pen(Color, penWidth))
            {
                pen.DashStyle = DashStyle;
                pen.StartCap = LineCap.Square;
                pen.EndCap = LineCap.Square;
                if (pen.DashStyle != DashStyle.Solid)
                {
                    float patternWidth = 0;
                    foreach (float w in pen.DashPattern)
                        patternWidth += w * pen.Width;
                    if (y == y1)
                        pen.DashOffset = (x - ((int)(x / patternWidth)) * patternWidth) / pen.Width;
                    else
                        pen.DashOffset = (y - ((int)(y / patternWidth)) * patternWidth) / pen.Width;
                }

                if (Style != LineStyle.Double)
                    g.DrawLine(pen, x, y, x1, y1);
                else
                {
                    // we have to correctly draw inner and outer lines of a double line
                    float g1 = gap1 ? pen.Width : 0;
                    float g2 = gap2 ? pen.Width : 0;
                    float g3 = -g1;
                    float g4 = -g2;

                    if (reverseGaps)
                    {
                        g1 = -g1;
                        g2 = -g2;
                        g3 = -g3;
                        g4 = -g4;
                    }

                    if (x == x1)
                    {
                        g.DrawLine(pen, x - pen.Width, y + g1, x1 - pen.Width, y1 - g2);
                        g.DrawLine(pen, x + pen.Width, y + g3, x1 + pen.Width, y1 - g4);
                    }
                    else
                    {
                        g.DrawLine(pen, x + g1, y - pen.Width, x1 - g2, y1 - pen.Width);
                        g.DrawLine(pen, x + g3, y + pen.Width, x1 - g4, y1 + pen.Width);
                    }
                }
            }
        }

        internal void Serialize(FRWriter writer, string prefix, BorderLine c)
        {
            if (Color != c.Color)
                writer.WriteValue(prefix + ".Color", Color);
            if (Style != c.Style)
                writer.WriteValue(prefix + ".Style", Style);
            if (Width != c.Width)
                writer.WriteFloat(prefix + ".Width", Width);
        }

        public BorderLine()
        {
            color = Color.Black;
            width = 1;
        }
        #endregion
    }

    /// <summary>
    /// Represents a border around the report object.
    /// </summary>
    /// <remarks>
    /// Border consists of four lines. Each line has own color, style and width. Lines are accessible through
    /// <see cref="LeftLine"/>, <see cref="RightLine"/>, <see cref="TopLine"/>, <see cref="BottomLine"/> properties.
    /// <para/>
    /// To turn on and off the lines, use the <see cref="Lines"/> property. To set the same color, style or width
    /// for each line, use <see cref="Color"/>, <see cref="Style"/>, <see cref="Width"/> properties of the <b>Border</b>.
    /// </remarks>
    [TypeConverter(typeof(FastReport.TypeConverters.FRExpandableObjectConverter))]
    [Editor("FastReport.TypeEditors.BorderEditor, FastReport", typeof(UITypeEditor))]
    public class Border
    {
        #region Fields
        private bool shadow;
        private float shadowWidth;
        private Color shadowColor;
        private BorderLines lines;
        private BorderLine leftLine;
        private BorderLine topLine;
        private BorderLine rightLine;
        private BorderLine bottomLine;
        private bool simpleBorder;
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets a color of the border.
        /// </summary>
        /// <remarks>
        /// This property actually returns a color of the <see cref="LeftLine"/>. When you assign a value 
        /// to this property, the value will be set to each border line.
        /// </remarks>
        [Editor("FastReport.TypeEditors.ColorEditor, FastReport", typeof(UITypeEditor))]
        public Color Color
        {
            get { return leftLine.Color; }
            set
            {
                leftLine.Color = value;
                rightLine.Color = value;
                topLine.Color = value;
                bottomLine.Color = value;
            }
        }

        /// <summary>
        /// Gets or sets a value determines whether to draw a shadow.
        /// </summary>
        [DefaultValue(false)]
        public bool Shadow
        {
            get { return shadow; }
            set { shadow = value; }
        }

        /// <summary>
        /// Gets or sets a shadow width, in pixels.
        /// </summary>
        [DefaultValue(4f)]
        public float ShadowWidth
        {
            get { return shadowWidth; }
            set { shadowWidth = value; }
        }

        /// <summary>
        /// Gets or sets a shadow color.
        /// </summary>
        [Editor("FastReport.TypeEditors.ColorEditor, FastReport", typeof(UITypeEditor))]
        public Color ShadowColor
        {
            get { return shadowColor; }
            set { shadowColor = value; }
        }

        /// <summary>
        /// Gets or sets a style of the border.
        /// </summary>
        /// <remarks>
        /// This property actually returns a style of the <see cref="LeftLine"/>. When you assign a value 
        /// to this property, the value will be set to each border line.
        /// </remarks>
        [DefaultValue(LineStyle.Solid)]
        [Editor("FastReport.TypeEditors.LineStyleEditor, FastReport", typeof(UITypeEditor))]
        public LineStyle Style
        {
            get { return leftLine.Style; }
            set
            {
                leftLine.Style = value;
                rightLine.Style = value;
                topLine.Style = value;
                bottomLine.Style = value;
            }
        }

        /// <summary>
        /// Gets or sets a visible lines of a border.
        /// </summary>
        [DefaultValue(BorderLines.None)]
        [Editor("FastReport.TypeEditors.BorderLinesEditor, FastReport", typeof(UITypeEditor))]
        public BorderLines Lines
        {
            get { return lines; }
            set { lines = value; }
        }

        /// <summary>
        /// Gets or sets a width of the border, in pixels.
        /// </summary>
        /// <remarks>
        /// This property actually returns a width of the <see cref="LeftLine"/>. When you assign a value 
        /// to this property, the value will be set to each border line.
        /// </remarks>
        [DefaultValue(1f)]
        public float Width
        {
            get { return leftLine.Width; }
            set
            {
                leftLine.Width = value;
                rightLine.Width = value;
                topLine.Width = value;
                bottomLine.Width = value;
            }
        }

        /// <summary>
        /// Gets or sets the left line of the border.
        /// </summary>
        public BorderLine LeftLine
        {
            get { return leftLine; }
            set { leftLine = value; }
        }

        /// <summary>
        /// Gets or sets the top line of the border.
        /// </summary>
        public BorderLine TopLine
        {
            get { return topLine; }
            set { topLine = value; }
        }

        /// <summary>
        /// Gets or sets the right line of the border.
        /// </summary>
        public BorderLine RightLine
        {
            get { return rightLine; }
            set { rightLine = value; }
        }

        /// <summary>
        /// Gets or sets the bottom line of the border.
        /// </summary>
        public BorderLine BottomLine
        {
            get { return bottomLine; }
            set { bottomLine = value; }
        }

        /// <summary>
        /// Gets or sets a value determines that <b>Border</b> must serialize only one line.
        /// </summary>
        /// <remarks>
        /// This property is for internal use only.
        /// </remarks>
        [Browsable(false)]
        public bool SimpleBorder
        {
            get { return simpleBorder; }
            set { simpleBorder = value; }
        }

        internal DashStyle DashStyle
        {
            get { return leftLine.DashStyle; }
        }
        #endregion

        #region Private Methods
        private bool ShouldSerializeLeftLine()
        {
            return LeftLine.ShouldSerialize();
        }

        private bool ShouldSerializeTopLine()
        {
            return TopLine.ShouldSerialize();
        }

        private bool ShouldSerializeRightLine()
        {
            return RightLine.ShouldSerialize();
        }

        private bool ShouldSerializeBottomLine()
        {
            return BottomLine.ShouldSerialize();
        }

        private bool ShouldSerializeColor()
        {
            return Color != Color.Black;
        }

        private bool ShouldSerializeShadowColor()
        {
            return ShadowColor != Color.Black;
        }
        #endregion

        #region Internal Methods

        internal void ZoomBorder(float zoom)
        {
            LeftLine.Width *= zoom;
            if (leftLine.Width > 0 && leftLine.Width < 1)
                LeftLine.Width = 1;

            RightLine.Width *= zoom;
            if (rightLine.Width > 0 && rightLine.Width < 1)
                RightLine.Width = 1;

            TopLine.Width *= zoom;
            if (topLine.Width > 0 && topLine.Width < 1)
                TopLine.Width = 1;

            BottomLine.Width *= zoom;
            if (bottomLine.Width > 0 && bottomLine.Width < 1)
                BottomLine.Width = 1;
        }

        #endregion

        #region Public Methods
        /// <summary>
        /// Creates the exact copy of this <b>Border</b>.
        /// </summary>
        /// <returns>A copy of this border.</returns>
        public Border Clone()
        {
            Border result = new Border(this);
            return result;
        }

        /// <inheritdoc/>
        public override bool Equals(object obj)
        {
            Border b = obj as Border;
            return b != null && Lines == b.Lines &&
              LeftLine.Equals(b.LeftLine) && TopLine.Equals(b.TopLine) &&
              RightLine.Equals(b.RightLine) && BottomLine.Equals(b.BottomLine) &&
              Shadow == b.Shadow && ShadowColor == b.ShadowColor && ShadowWidth == b.ShadowWidth;
        }

        /// <inheritdoc/>
        public override int GetHashCode()
        {
            return Lines.GetHashCode() ^ (Shadow.GetHashCode() + 16) ^ ShadowColor.GetHashCode() ^
              (ShadowWidth.GetHashCode() + 32) ^ (LeftLine.GetHashCode() << 1) ^ (TopLine.GetHashCode() << 2) ^
              (RightLine.GetHashCode() << 3) ^ (BottomLine.GetHashCode() << 4);
        }

        /// <summary>
        /// Serializes the border.
        /// </summary>
        /// <param name="writer">Writer object.</param>
        /// <param name="prefix">Border property name.</param>
        /// <param name="c">Another Border to compare with.</param>
        /// <remarks>
        /// This method is for internal use only.
        /// </remarks>
        public void Serialize(FRWriter writer, string prefix, Border c)
        {
            if (Shadow != c.Shadow)
                writer.WriteBool(prefix + ".Shadow", Shadow);
            if (ShadowWidth != c.ShadowWidth)
                writer.WriteFloat(prefix + ".ShadowWidth", ShadowWidth);
            if (ShadowColor != c.ShadowColor)
                writer.WriteValue(prefix + ".ShadowColor", ShadowColor);
            if (!SimpleBorder)
            {
                if (Lines != c.Lines)
                    writer.WriteValue(prefix + ".Lines", Lines);
                if (Lines != BorderLines.None || Color != Color.Black)
                {
                    if (LeftLine.Equals(RightLine) && LeftLine.Equals(TopLine) && LeftLine.Equals(BottomLine) &&
                      c.LeftLine.Equals(c.RightLine) && c.LeftLine.Equals(c.TopLine) && c.LeftLine.Equals(c.BottomLine))
                        LeftLine.Serialize(writer, prefix, c.LeftLine);
                    else
                    {
                        LeftLine.Serialize(writer, prefix + ".LeftLine", c.LeftLine);
                        TopLine.Serialize(writer, prefix + ".TopLine", c.TopLine);
                        RightLine.Serialize(writer, prefix + ".RightLine", c.RightLine);
                        BottomLine.Serialize(writer, prefix + ".BottomLine", c.BottomLine);
                    }
                }
            }
            else
                LeftLine.Serialize(writer, prefix, c.LeftLine);
        }

        /// <summary>
        /// Draw the border using draw event arguments and specified bounding rectangle.
        /// </summary>
        /// <param name="e">Draw event arguments.</param>
        /// <param name="rect">Bounding rectangle.</param>
        /// <remarks>
        /// This method is for internal use only.
        /// </remarks>
        public void Draw(FRPaintEventArgs e, RectangleF rect)
        {
            IGraphics g = e.Graphics;
            rect.X *= e.ScaleX;
            rect.Y *= e.ScaleY;
            rect.Width *= e.ScaleX;
            rect.Height *= e.ScaleY;

            if (Shadow)
            {
                //int d = (int)Math.Round(ShadowWidth * e.ScaleX);
                //Pen pen = e.Cache.GetPen(ShadowColor, d, DashStyle.Solid);
                //g.DrawLine(pen, rect.Right + d / 2, rect.Top + d, rect.Right + d / 2, rect.Bottom);
                //g.DrawLine(pen, rect.Left + d, rect.Bottom + d / 2, rect.Right + d, rect.Bottom + d / 2);

                float d = ShadowWidth * e.ScaleX;
                Brush brush = e.Cache.GetBrush(ShadowColor);
                g.FillRectangle(brush, rect.Left + d, rect.Bottom, rect.Width, d);
                g.FillRectangle(brush, rect.Right, rect.Top + d, d, rect.Height);
            }

            if (Lines != BorderLines.None)
            {
                // draw full frame as a rectangle with solid line only. Non-solid lines
                // should be drawn separately to avoid overlapping effect
                if (Lines == BorderLines.All && LeftLine.Equals(TopLine) && LeftLine.Equals(RightLine) &&
                  LeftLine.Equals(BottomLine) && LeftLine.Style == LineStyle.Solid)
                {
                    Pen pen = e.Cache.GetPen(LeftLine.Color, (int)Math.Round(LeftLine.Width * e.ScaleX), LeftLine.DashStyle);
                    g.DrawRectangle(pen, rect.Left, rect.Top, rect.Width, rect.Height);
                }
                else
                {
                    if ((Lines & BorderLines.Left) != 0)
                        LeftLine.Draw(e, rect.Left, rect.Top, rect.Left, rect.Bottom,
                          true, (Lines & BorderLines.Top) != 0, (Lines & BorderLines.Bottom) != 0);
                    if ((Lines & BorderLines.Right) != 0)
                        RightLine.Draw(e, rect.Right, rect.Top, rect.Right, rect.Bottom,
                          false, (Lines & BorderLines.Top) != 0, (Lines & BorderLines.Bottom) != 0);
                    if ((Lines & BorderLines.Top) != 0)
                        TopLine.Draw(e, rect.Left, rect.Top, rect.Right, rect.Top,
                          true, (Lines & BorderLines.Left) != 0, (Lines & BorderLines.Right) != 0);
                    if ((Lines & BorderLines.Bottom) != 0)
                        BottomLine.Draw(e, rect.Left, rect.Bottom, rect.Right, rect.Bottom,
                          false, (Lines & BorderLines.Left) != 0, (Lines & BorderLines.Right) != 0);
                }
            }
        }
        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="Border"/> class with default settings. 
        /// </summary>
        public Border()
        {
            leftLine = new BorderLine();
            topLine = new BorderLine();
            rightLine = new BorderLine();
            bottomLine = new BorderLine();
            shadowWidth = 4;
            shadowColor = Color.Black;
        }

        private Border(Border src)
        {
            lines = src.Lines;
            shadow = src.Shadow;
            shadowColor = src.ShadowColor;
            shadowWidth = src.ShadowWidth;
            leftLine = src.LeftLine.Clone();
            topLine = src.TopLine.Clone();
            rightLine = src.RightLine.Clone();
            bottomLine = src.BottomLine.Clone();
            //Fix 1513
            //Width = src.Width;
            //1513
        }
    }
}